#include <stdio.h>
#include <math.h>
#include <CL/cl.h>
#include "aux.h"

int main(void)
{
    // Get Platform
    cl_platform_id platform;
    clGetPlatformIDs(1, &platform, NULL);
    printf("...First platform chosen\n");

    // Get device
    cl_device_id device;
    clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL);
    printf("...First device chosen\n");

    // Context Creation
    cl_context context;
    cl_int err;
    context = clCreateContext(NULL, 1, &device, NULL, NULL, &err);
    if (err==CL_SUCCESS){
        printf("Context created\n");
    } else {
        printf("Error creating context\n");
    }

    // Program Objects:
    const char* programSource = readKernel();
    cl_program program;
    program = clCreateProgramWithSource(context, 1,
                                        (const char**)&programSource,
                                        NULL, &err);
    if (err==CL_SUCCESS){
        printf("Program created\n");
    } else {
        printf("Error creating program\n");
    }

    err = clBuildProgram(program, 1, &device, NULL, NULL, NULL);
    if (err==CL_SUCCESS){
        printf("Program compiled\n");
    } else {
        printf("Error compiling program\n");
        printf("%i\n", err);
    }

    cl_kernel kernel;
    kernel = clCreateKernel(program, "pi_calc", &err);
    if (err==CL_SUCCESS){
        printf("Kernel created\n");
    } else {
        printf("Error creating kernel\n");
    }

    // Host Data
    float arrayHost[SIZE];

    // Memory Objects:
    cl_mem arrayDevice;
    arrayDevice = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(float)*SIZE,
                                 NULL, &err);
    if (err==CL_SUCCESS){
        printf("Buffer A created\n");
    } else {
        printf("Error creating buffer A\n");
    }

    // Set Kernel Arguments
    cl_float delta = DELTA;
    // Compute log2 outside the device
    cl_int log_size = log2(SIZE);
    err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &arrayDevice);
    err |= clSetKernelArg(kernel, 1, sizeof(cl_float), &delta);
    err |= clSetKernelArg(kernel, 2, sizeof(cl_int), &log_size);
    if (err==CL_SUCCESS){
        printf("Kernel's arguments configured\n");
    } else {
        printf("Error configuring the kernel's arguments\n");
        printf("%d\n", err);
    }

    // Create Command Queue
    cl_command_queue commands;
    commands = clCreateCommandQueue(context, device, 0, &err);
    if (err==CL_SUCCESS){
        printf("Command queue created\n");
    } else {
        printf("Error creating command queue\n");
    }

    // Memory Write
//     err = clEnqueueWriteBuffer(commands, arrayDevice, CL_TRUE, 0,
//                                sizeof(float)*SIZE, arrayHost, 0, NULL, NULL);
//     if (err==CL_SUCCESS){
//         printf("Buffer A copied to device\n");
//     } else {
//         printf("Error copying buffer A to device\n");
//     }

    // Kernel Execution
    size_t global = SIZE;
    size_t local = SIZE;
    err = clEnqueueNDRangeKernel(commands, kernel, 1, NULL, &global, &local, 0,
                                 NULL, NULL);
    if (err==CL_SUCCESS){
        printf("Kernel sent to device\n");
    } else {
        printf("Error sending kernel to device\n");
        printf("%d\n", err);
    }

    clFinish(commands);

    // Memory Read
    err = clEnqueueReadBuffer(commands, arrayDevice, CL_TRUE, 0,
                              sizeof(float)*SIZE, arrayHost, 0, NULL, NULL);
    if (err==CL_SUCCESS){
        printf("Buffer C copied from device\n");
    } else {
        printf("Error copying buffer C from device\n");
    }

    for (int i=0; i<SIZE; i++){
        // ANDA pero no se porque!!!
        printf("%f\n", arrayHost[i]*DELTA);
    }

    return 0;
}
